<?php

namespace app\admin\common\Http;

use think\log;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\ClientException;//客户端错误，如404
use GuzzleHttp\Exception\ConnectException;//网络错误,连接异常
use GuzzleHttp\Exception\RequestException;//请求异常
use GuzzleHttp\Exception\ServerException;//服务内部错误,如500
use GuzzleHttp\Exception\TooManyRedirectsException;//重定向次数过多错误

use app\admin\common\Http\Exceptions\DataValidationFailedException;//自定义错误（数据验证异常）


/**
 * 自定义接口请求客户端
 */
class ConferenceHttpClient
{

    /**
     * 本类内置的返回信息
     */
    //json_decode函数解析失败
    const ERROR_CODE_JSON_DECODE_FAIL = 5001;
    const ERROR_MESSAGE_JSON_DECODE_FAIL = '返回值不正确，JSON解析失败！';
    //没有找到java返回的code和msg字段,表示返回值依然不正确
    const ERROR_CODE_NO_FIELD = 5002;
    const ERROR_MESSAGE_NO_FIELD = '返回数据不正确！';

    /**
     * 错误代码
     * @var int
     */
    public $errorCode = null;
    /**
     * 错误消息
     * @var string
     */
    public $errorMessage = null;

    /**
     * 是否返回全部数据（默认返回$json->obj节点）
     * @var bool
     */
    private $isReturnAll = false;

    /**
     * 是否关闭日志记录
     * @var bool
     */
    private $isCloseLog = false;


    private $HttpStatusCode;

//    private $baseuri = config('baseuri');

    private $config=array(
        'base_uri'          => BASEURI,
        'timeout'=>TIMEOUT,
        'http_errors'=>false,//false 不显示异常 true 显示异常
        'allow_redirects'=>true,//false 禁止重定向 true 允许重定向
        'headers' => [
            'accept'     => 'application/json',
            'Authorization' => 'Bearer  ',
            'X-App-Localization' => 'zh-CN',
            'Content-Type' => 'application/x-www-form-urlencoded',
        ]
    );

    private $client;
    private $responseFormat='JSON';//INT STRING HTML

    /**
     * 会务系统专用接口
     * @var
     */

    public function __construct(){
        $config=$this->config;
        $this->client = new \GuzzleHttp\Client($config);
    }

    public function get($url,$data=array(),$headers=array()){
        $config=$this->config;
        $config['query']=$data;
        if($headers){
            $config['headers']['Authorization'] = 'Bearer  '.$headers['Authorization'];
            if(isset($headers['lang'])) {
                $config['headers']['X-App-Localization'] = $headers['lang'];
            }
            if(isset($headers['AuthToken'])) {
                $config['headers']['X-App-Auth-Token'] = $headers['AuthToken'];

                $config['headers']['X-App-Authorize-Token'] = $headers['AuthorizeToken'];

            }
        }
        $res = self::request('GET', $url,$config);
        return $res;
    }

    public function post($url,$data=array(),$headers=array()){
        $config=$this->config;
        $config['form_params']=$data;
        if($headers){
            $config['headers']['Authorization'] = 'Bearer  '.$headers['Authorization'];
            if(isset($headers['lang'])) {

                $config['headers']['X-App-Localization'] = $headers['lang'];
            }
            if(isset($headers['AuthToken'])) {
//                var_dump(111);
                $config['headers']['X-App-Auth-Token'] = $headers['AuthToken'];

                $config['headers']['X-App-Authorize-Token'] = $headers['AuthorizeToken'];

            }
        }
        $res = self::request('POST', $url,$config);
        return $res;
    }

    public function put($url,$data=array(),$headers=array()){
        $config=$this->config;
        $config['form_params']=$data;
        if($headers){
            $config['headers']['Authorization'] = 'Bearer  '.$headers['Authorization'];
            if(isset($headers['lang'])) {
                $config['headers']['X-App-Localization'] = $headers['lang'];
            }
            if(isset($headers['AuthToken'])) {
                $config['headers']['X-App-Auth-Token'] = $headers['AuthToken'];
                $config['headers']['X-App-Authorize-Token'] = $headers['AuthorizeToken'];
            }
        }
        $res = self::request('Put', $url,$config);
        return $res;
    }

    public function delete($url,$data=array(),$headers=array()){
        $config=$this->config;
        $config['query']=$data;
        if($headers){
            $config['headers']['Authorization'] = 'Bearer  '.$headers['Authorization'];
            if(isset($headers['lang'])) {

                $config['headers']['X-App-Localization'] = $headers['lang'];
            }
            if(isset($headers['AuthToken'])) {
//                var_dump(111);
                $config['headers']['X-App-Auth-Token'] = $headers['AuthToken'];

                $config['headers']['X-App-Authorize-Token'] = $headers['AuthorizeToken'];

            }
        }
        $res = self::request('DELETE', $url,$config);
        return $res;
    }

    public function json($url,$data=array()){
        $config=$this->config;
        $config['json']=$data;
        $res = self::request('POST', $url, $config);
        return $res;
    }


    /**
     * 通用｛同步｝请求方法（包含异常处理、数据验证等）
     * @param string $method
     * @param string $uri
     * @param array $options
     * @return bool
     */
    public function request(string $method, string $uri = '', array $config = []){
        //$client = new Client($this->config);
        try {
            /** @var \Psr\Http\Message\ResponseInterface $res */
            $res = $this->client->request($method , $uri, $config);
            //dump($res->getBody()->getContents());exit();
            $json = $this->validate($res);

        }
        catch (ClientException $e) {
            $this->error($e->getCode(), '未能找到资源');
            return $this->exception($method, $uri, $config, $e);
        }
        catch (ConnectException $e) {
            $this->error($e->getCode(), '网络错误,连接异常');
            return $this->exception($method, $uri, $config, $e);
        }
        catch (RequestException $e) {
            $this->error($e->getCode(), '请求异常');
            return $this->exception($method, $uri, $config, $e);
        }
        catch (ServerException $e) {
            $this->error($e->getCode(), '服务内部错误');
            return $this->exception($method, $uri, $config, $e);
        }
        catch (TooManyRedirectsException $e) {
            $this->error($e->getCode(), '重定向次数过多');
            return $this->exception($method, $uri, $config, $e);
        }
        catch (DataValidationFailedException $e) {
            //注意数据检测失败异常只记录日志，类返回错误已在validate中定义
            return $this->exception($method, $uri, $config, $e);
        }
        catch (Exception $e) {
            $this->error($e->getCode(), $e->getMessage());
            return $this->exception($method, $uri, $config, $e);
        }
        return $json;
    }

//    public function request($method,$url,$config){
//
//        $resData=array('status'=>true,'code'=>200,'msg'=>'','obj'=>'');
//        try{
//            $res = $this->client->request($method, $url, $config);
////            $body     = $res->getBody()->getContents();//获取请求结果
////            $json     = json_decode($body);
////            var_dump($config);die();
//            $httpStatusCode=$res->getStatusCode();
//            self::setHttpStatusCode($httpStatusCode);
//            if($httpStatusCode !== 200 && $httpStatusCode !== 201){
//                $resData['status']=false;
//                $resData['code']=$httpStatusCode;
//                $resData['msg']='服务器异常';
//                return $resData;
//            }
//            $body=$res->getBody()->getContents();
//            if(empty($body)){
//                $resData['status']=false;
//                $resData['msg']='返回为空';
//                return $resData;
//            }
//
//            $ResponseFormat=self::getEnv('responseFormat');
//            if($ResponseFormat==='JSON'){
//                $array=json_decode($body);
//                if(is_null($array)){
//                    $resData['status']=false;
//                    $resData['msg']='返回格式异常';
//                    return $resData;
//                }else{
//                    $resData['obj']=(object)$array;
//                }
//            }else{
//                $resData['obj']=$body;
//            }
//            return $resData;
//        }catch(RequestException $e){
//            $resData['status']=false;
//            $resData['msg']='请求超时';
////            $txt=$e->getHandlerContext();
////            if($txt['errno']===28){ //超时
////                $resData['msg']='请求超时';
////            }
//
//            if($e->hasResponse()){
//                // echo  $e->getResponse();
//            }
//            return $resData;
//        }
//
//         $this->setHttpStatusCode($res->getStatusCode());
//         $body     = $res->getBody()->getContents();//获取请求结果
//         $json     = json_decode($body);
//         return $json;
//    }

    public function sendAsy($method,$url,$data=array()){
        $config=$this->config;
        $this->setConfig('timeout',0.01);
        $res = self::$method( $url, $data);
    }

//    public function verificationRes($res){
//        if(is_object($res)){
//
//        }
//    }
//
//    public function setHttpStatusCode($HttpStatusCode){
//        $this->HttpStatusCode=$HttpStatusCode;
//    }
//
//    public function getHttpStatusCode(){
//        return $this->HttpStatusCode;
//    }
//
//    public function setConfig($name='',$value=''){
//        if($name==''){
//            $this->config=$value;
//        }
//        $config=$this->config;
//        $config[$name]=$value;
//        $this->config=$config;
//    }
//    public function getConfig($value=''){
//        $config=$this->config;
//        if($value==''){
//            return $config;
//        }
//        if(isset($config[$value])){
//            return $config[$value];
//        }
//        return false;
//    }
//
//    public function setEnv($name,$value){
//        if(isset($this->$name)){
//            $this->$name=$value;
//        }
//    }
//
//    public function getEnv($name){
//        return $this->$name;
//    }
//
//    public function setMoblieRequest(){
//        $config=$this->config;
//        $config['headers']['User-Agent']=sprintf('%s %s',$config['headers']['User-Agent'],'iPhone');
//        $this->config=$config;
//    }


    /**
     * 数据验证
     * @param ResponseInterface $res
     * @return
     * @throws DataValidationFailedException
     */
    private function validate(ResponseInterface $res){
             $body     = $res->getBody()->getContents();//获取请求结果
             $json     = json_decode($body);
        //解析不正常
        if (!is_object($json)) {
            //设置类的错误消息，调用接口显示
            $this->error(self::ERROR_CODE_JSON_DECODE_FAIL, self::ERROR_MESSAGE_JSON_DECODE_FAIL);
            throw new DataValidationFailedException('validate method fail: json_decode fail,json_last_error:'.json_last_error(),500);
        }
        //返回值不正常
        if(!isset($json->code)){
            //设置类的错误消息，调用接口显示
            $this->error(self::ERROR_CODE_NO_FIELD, self::ERROR_MESSAGE_NO_FIELD);
            throw new DataValidationFailedException('validate method fail: no code and msg',500);
        }

        //返回码不正常
//        if($json->code != 0 and $json->code != 1 and $json->code != 200){
//            //设置类的错误消息，调用接口显示
//            $this->error($json->code, $json->msg);
//            throw new DataValidationFailedException($json->msg,$json->code);
//        }
        //返回全部节点
        if($this->isReturnAll){
            return $json;
        }
        return $json;
    }

    /**
     * 异常日志记录
     * @param $method
     * @param string $uri
     * @param array $options
     * @param \Exception $e
     * @return boolean
     */
    private function exception(string $method, string $uri = '', array $options = [], Exception $e){
        //新增日志开关 @jmj 20170704
        if($this->isCloseLog !== true){
            $message = [
                'Uri' => rtrim($this->config['base_uri'],'/'). '/' . $uri,
                'Client' => self::class,
                'Method' => $method,
                'Params' => $options,
                'Code' => $e->getCode(),
                'File' => $e->getFile(),
                'Line' => $e->getLine(),
                //'Trace' => $e->getTraceAsString(),//堆栈太多，尽量不要
                'Message' => trim($e->getMessage()),
            ];
            Log::error(json_encode($message));
        }

        return false;//发生异常时，统一返回值为 false
    }

    /**
     * 设置类错误消息
     * @param $code
     * @param $message
     */
    private function error($code, $message){
        $this->errorCode = $code;
        $this->errorMessage = $message;
    }



}